#include "protocol.h"

Sockaddr *
resolveHost(register char *name)
{
	register struct hostent *fhost;
	struct in_addr fadd;
	static Sockaddr sa;

	if ((fhost = gethostbyname(name)) != NULL) {
		sa.sin_family = fhost->h_addrtype;
		sa.sin_port = 0;
		bcopy(fhost->h_addr, &sa.sin_addr, fhost->h_length);
	} else {
		fadd.s_addr = inet_addr(name);
		if (fadd.s_addr != -1) {
			sa.sin_family = AF_INET;	/* grot */
			sa.sin_port = 0;
			sa.sin_addr.s_addr = fadd.s_addr;
		} else
			return(NULL);
	}
	return(&sa);
}

void
NextEvent(MWEvent *event, int socket)
{
//   fd_set	fdmask;
//   XEvent	xev;
//   int	ret;
//   char	c;

  while (1) {
      //if (FD_ISSET(socket, &fdmask))
// 	{
	  socklen_t fromLen = sizeof(event->eventSource);
	  int cc;
	  cc = recvfrom(socket, (char*)event->eventDetail,
			sizeof(LongPacket), 0,
		        (Sockaddr*)&(event->eventSource),
			&fromLen);
	  if (cc <= 0)
	    {
	      if (cc < 0 && errno != EINTR)
		perror("event recvfrom");
	      continue;
	    }
	  if (fromLen != sizeof(struct sockaddr_in))
	    continue;
// 	  ConvertIncoming((ShortPacket*)event->eventDetail);
	  return;
// 	}
    }
}

void MWError(char *s)
{
// 	StopWindow();
	fprintf(stderr, "DFS: %s\n", s);
	perror("DFS");
	exit(-1);
}

// The checksum calculation method is referred to the code of Roger Liao <rogliao@cs.stanford.edu>
uint16_t fillChecksum(LongPacket* pack)
{
  pack->header.checksum = 0;
  
  uint8_t *data = (uint8_t*)pack;
  uint32_t sum;
  int len = HEADER_LEN + pack->header.paylen;

  for (sum = 0; len >= 2; data += 2, len -= 2)
    sum += data[0] << 8 | data[1];
  if (len > 0)
    sum += data[0] << 8;
  while (sum > 0xffff)
    sum = (sum >> 16) + (sum & 0xffff);
  
  sum = ~sum;
  pack->header.checksum = sum ? sum : 0xffff;
  return pack->header.checksum;
}

uint8_t checkSumRight(ShortPacket pack)
{
  uint16_t checksum = pack.header.checksum;
  if (fillChecksum((LongPacket*)&pack) == checksum)
  {
    return 1;
  }
  printf("Error wrong checksum");
  return 0;
}

void netInit(Sockaddr *myAddr, uint32_t* my_socket_p, Sockaddr* groupAddr, int portNum)
{
  	Sockaddr	nullAddr;
	Sockaddr	*thisHost;
	char		buf[128];
	int		reuse;
	u_char          ttl;
	struct ip_mreq  mreq;

// 	myAddr = (Sockaddr*)malloc(sizeof(Sockaddr));
	if(!myAddr) {
		printf("Error allocating sockaddr variable");
	}
	getaddrinfo();
	gethostname(buf, sizeof(buf));
// 	printf("my name is: %s",buf);
	if ((thisHost = resolveHost(buf)) == (Sockaddr *) NULL)
	  MWError("who am I?");
	memcpy((caddr_t) myAddr, (caddr_t) thisHost, sizeof(Sockaddr));

	uint32_t my_socket = socket(AF_INET, SOCK_DGRAM, 0);
	if (my_socket < 0)
	  MWError("can't get socket");

	/* SO_REUSEADDR allows more than one binding to the same
	   socket - you cannot have more than one player on one
	   machine without this */
	reuse = 1;
	if (setsockopt(my_socket, SOL_SOCKET, SO_REUSEADDR, &reuse,
		   sizeof(reuse)) < 0) {
		MWError("setsockopt failed (SO_REUSEADDR)");
	}

	nullAddr.sin_family = AF_INET;
	nullAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	nullAddr.sin_port = portNum;
	if (bind(my_socket, (struct sockaddr *)&nullAddr,
		 sizeof(nullAddr)) < 0)
	  MWError("netInit binding");

	/* Multicast TTL:
	   0 restricted to the same host
	   1 restricted to the same subnet
	   32 restricted to the same site
	   64 restricted to the same region
	   128 restricted to the same continent
	   255 unrestricted

	   DO NOT use a value > 32. If possible, use a value of 1 when
	   testing.
	*/

	ttl = 32;
	if (setsockopt(my_socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
		   sizeof(ttl)) < 0) {
		MWError("setsockopt failed (IP_MULTICAST_TTL)");
	}

	/* uncomment the following if you do not want to receive messages that
	   you sent out - of course, you cannot have multiple players on the
	   same machine if you do this */
#if 0
	{
	  u_char loop = 0;
	  if (setsockopt(*my_socket, IPPROTO_IP, IP_MULTICAST_LOOP, &loop,
	             sizeof(loop)) < 0) {
		MWError("setsockopt failed (IP_MULTICAST_LOOP)");
	  }
	}
#endif

	/* join the multicast group */
	mreq.imr_multiaddr.s_addr = htonl(MAZEGROUP);
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);
	if (setsockopt(my_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)
		   &mreq, sizeof(mreq)) < 0) {
		MWError("setsockopt failed (IP_ADD_MEMBERSHIP)");
	}
	
	/* Get the multi-cast address ready to use in SendData()
           calls. */
// 	groupAddr = (Sockaddr*)malloc(sizeof(Sockaddr));
	memcpy(my_socket_p, &my_socket, sizeof(uint32_t));
	memcpy(groupAddr, &nullAddr, sizeof(Sockaddr));
	groupAddr->sin_addr.s_addr = htonl(MAZEGROUP);
}
